package cn.dslcode.security.config.oauth2;

import cn.dslcode.security.config.CustomAuthenticationHandlers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.ApprovalStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.sql.DataSource;

/**
 * @author dongsilin
 * @version 2018/5/15.
 */
@Configuration
public class OAuth2BeanConfiguration {

    @Autowired
    private DataSource dataSource;
    @Autowired
    private RedisConnectionFactory connectionFactory;
    @Autowired
    private CustomAuthenticationHandlers authenticationHandlers;

    private PasswordEncoder passwordEncoder;
    private ClientDetailsService clientDetailsService;
    private TokenStore tokenStore;
    private ApprovalStore approvalStore;
    private AuthorizationCodeServices authorizationCodeServices;
    private AccessDeniedHandler oauth2AccessDeniedHandler;
    private AuthenticationEntryPoint oauth2AuthenticationEntryPoint;
    private OAuth2RequestFactory oAuth2RequestFactory;
    private UserApprovalHandler userApprovalHandler;


    @Bean
    public PasswordEncoder passwordEncoder() {
        return passwordEncoder == null ? passwordEncoder = new BCryptPasswordEncoder(8) : passwordEncoder;
    }

    @Bean
    public ClientDetailsService clientDetailsService() {
        return clientDetailsService == null ? clientDetailsService = new JdbcClientDetailsService(dataSource) : clientDetailsService;
    }

    @Bean
    public TokenStore tokenStore() {
        return tokenStore == null ? tokenStore = new RedisTokenStore(connectionFactory) : tokenStore;
    }

    @Bean
    public ApprovalStore approvalStore() {
        return approvalStore == null ? approvalStore = new JdbcApprovalStore(dataSource) : approvalStore;
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return authorizationCodeServices == null ? authorizationCodeServices = new JdbcAuthorizationCodeServices(dataSource) : authorizationCodeServices;
    }

    @Bean
    public AccessDeniedHandler oauth2AccessDeniedHandler() {
        return oauth2AccessDeniedHandler == null ? oauth2AccessDeniedHandler = new OAuth2AccessDeniedHandler() : oauth2AccessDeniedHandler;
    }

    @Bean
    public AuthenticationEntryPoint oauth2AuthenticationEntryPoint() {
        return oauth2AuthenticationEntryPoint == null ? oauth2AuthenticationEntryPoint = new OAuth2AuthenticationEntryPoint() : oauth2AuthenticationEntryPoint;
    }

    @Bean
    public OAuth2RequestFactory oAuth2RequestFactory() {
        return oAuth2RequestFactory == null ? oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService()) : oAuth2RequestFactory;
    }

    @Bean
    public UserApprovalHandler userApprovalHandler() {
        return userApprovalHandler == null ?
            userApprovalHandler = new ApprovalStoreUserApprovalHandler() {
                {
                    setClientDetailsService(clientDetailsService());
                    setApprovalStore(approvalStore());
                    setRequestFactory(oAuth2RequestFactory());
                }
            } :
            userApprovalHandler;
    }

}
